try:
    import binascii
except:
    import ubinascii as binascii
import _thread


class SerialNumber(object):
    __lock__ = _thread.allocate_lock()
    __number__ = 0

    @classmethod
    def get(cls):
        with cls.__lock__:
            if cls.__number__ == 0xFFFF:
                cls.__number__ = 0
                return cls.__number__
            cls.__number__ += 1
            return cls.__number__


class Message(object):

    def __init__(self, addr, cmd, data, serial_number=None, checksum=None):
        """Base Message Constructor

        Args:
            addr (int): 地址（0-65535）
            cmd (int): 控制命令
            serial_number (int): 帧序号（0-255），后台每发一条命令自动加1，硬件返回上次收到的帧号
            length (int): 长度（0-65535）1024  --16*60
            data (bytes): 数据内容
            checksum (int): CRC校验
        """
        self.addr = addr
        self.cmd = cmd
        self.serial_number = serial_number if serial_number is not None else SerialNumber.get()
        self.data = data
        self.checksum = checksum

    def __str__(self):
        return self.toHex()

    def genCrcValue(self, data):
        crc = 0xFFFF
        for byte in data:
            crc ^= byte
            for _ in range(8):
                if (crc & 0x0001) != 0:
                    crc >>= 1
                    crc ^= 0xA001
                else:
                    crc >>= 1
        # Swap the two bytes of the CRC
        crc_high = (crc >> 8) & 0xFF
        crc_low = crc & 0xFF
        return crc_high << 8 | crc_low
    
    def toBytes(self):
        raw = bytearray([0xFA, 0xF5])
        raw += self.addr.to_bytes(2, 'big')
        raw += self.cmd.to_bytes(1, 'big')
        raw += self.serial_number.to_bytes(1, 'big')
        raw += len(self.data).to_bytes(2, 'big')
        raw += self.data
        if self.checksum is None:
            self.checksum = self.genCrcValue(raw)
        raw += self.checksum.to_bytes(2, 'big')
        return raw

    @classmethod
    def fromBytes(cls, raw):
        if not (raw[0] == 0xFA and raw[1] == 0xF5):
            raise ValueError("Invalid Header")
        addr = int.from_bytes(raw[2:4], 'big')
        cmd = raw[4]
        serial_number = raw[5]
        length = int.from_bytes(raw[6:8], 'big')
        data = raw[8:8+length]
        checksum = int.from_bytes(raw[-2:], 'big')
        return cls(addr, cmd, data, serial_number, checksum)

    def toHex(self):
        return binascii.hexlify(self.toBytes()).decode().upper()

    @classmethod
    def fromHex(cls, string):
        return cls.fromBytes(binascii.unhexlify(string))


# 模组请求
class Request(Message):
    ADDR = 0x0000
    CMD = 0x01

    def __init__(self, data):
        if isinstance(data, str):
            data = data.encode()
        super().__init__(self.ADDR, self.CMD, data)


# 模组状态通知
class StatusNotifyRequest(Request):
    ADDR = 0x0000
    CMD = 0x01


# 模组应答
class Response(Message):
    ADDR = 0x0000
    CMD = 0x02

    def __init__(self, serial_number, data):
        """模组应答

        Args:
            serial_number (int): 帧序号
            data (bytes): 数据内容
        """
        if isinstance(data, str):
            data = data.encode()
        super().__init__(self.ADDR, self.CMD, data, serial_number)


class ServerAddrConfigResponse(Response):
    ADDR = 0x0000
    CMD = 0x02


class RecordReportPathResponse(Response):
    ADDR = 0x0000
    CMD = 0x03


class RecordReceivePathResponse(Response):
    ADDR = 0x0000
    CMD = 0x04


class RecordStartResponse(Response):
    ADDR = 0x0000
    CMD = 0x05


class PlayAudioResponse(Response):
    ADDR = 0x0000
    CMD = 0x06


class QueryInfoResponse(Response):
    ADDR = 0x0000
    CMD = 0x07


class StartVoiceCalllResponse(Response):
    ADDR = 0x0000
    CMD = 0x08


class StopVoiceCalllResponse(Response):
    ADDR = 0x0000
    CMD = 0x08


if __name__ == '__main__':
    # 应答响应对象构造
    print("模组状态通知：", StatusNotifyRequest("18588269037"))
    print("服务器地址配置: ", ServerAddrConfigResponse(1, "www.bysdmina.cn/8100"))
    print("录音上报路径（HTTP）:", RecordReportPathResponse(2, "http://tools.openluat.com/api/site/device_upload_file"))
    print("录音接收录音播报路径（HTTP）:", RecordReceivePathResponse(3, "http://tools.openluat.com/api/site/device_upload_file"))
    print("控制模组进行语音启动录音: ", RecordStartResponse(4, b''))
    print("控制模组音频播放: ", PlayAudioResponse(6, b''))
    print("查询信息：需要返回GPS信息、WIFI MAC地址、基站信号强度信息: ", QueryInfoResponse(7, b"xxxxxxxxxxxxxxxxxx"))
    print("拨打电话: ", StartVoiceCalllResponse(8, b'\x01'))
    print("挂断电话: ", StopVoiceCalllResponse(9, b'\x00'))

    # 转成字节
    resp = StatusNotifyRequest("18588269037")
    print("模组状态通知（字节）：", resp.toBytes())
